#region

using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Threading;

#endregion

namespace System.Collections.Generic
{
  /// <summary>
  ///   A generic list that provides event for changes to the list.
  /// </summary>
  /// <typeparam name = "T">Type for the list.</typeparam>
  [Serializable]
  public class EventedList<T> : IList<T>, IList
  {
    // Fields
    private const int _defaultCapacity = 4;

    private static readonly T[] _emptyArray;

    private T[] _items;
    private int _size;
    [NonSerialized] private object _syncRoot;
    private int _version;

    /// <summary>
    ///   Initializes the <see cref = "EventedList&lt;T&gt;" /> class.
    /// </summary>
    static EventedList()
    {
      _emptyArray = new T[0];
    }

    /// <summary>
    ///   Initializes a new instance of the <see cref = "EventedList&lt;T&gt;" /> class.
    /// </summary>
    protected EventedList()
    {
      _items = _emptyArray;
    }

    /// <summary>
    ///   Initializes a new instance of the <see cref = "EventedList&lt;T&gt;" /> class.
    /// </summary>
    /// <param name = "collection">The collection.</param>
    public EventedList(IEnumerable<T> collection)
    {
      if (collection == null)
      {
        throw new ArgumentNullException("collection");
      }
      var is2 = collection as ICollection<T>;
      if (is2 != null)
      {
        var count = is2.Count;
        _items = new T[count];
        is2.CopyTo(_items, 0);
        _size = count;
      }
      else
      {
        _size = 0;
        _items = new T[4];
        using (var enumerator = collection.GetEnumerator())
        {
          while (enumerator.MoveNext())
          {
            Add(enumerator.Current);
          }
        }
      }
    }

    /// <summary>
    ///   Initializes a new instance of the <see cref = "EventedList&lt;T&gt;" /> class.
    /// </summary>
    /// <param name = "capacity">The capacity.</param>
    public EventedList(int capacity)
    {
      if (capacity < 0)
      {
        throw new ArgumentOutOfRangeException("capacity");
      }
      _items = new T[capacity];
    }

    /// <summary>
    ///   Occurs when an item has been added.
    /// </summary>
    public event EventHandler<ListChangedEventArgs<T>> ItemAdded;

    /// <summary>
    ///   Occurs when an item has changed.
    /// </summary>
    public event EventHandler<ListChangedEventArgs<T>> ItemChanged;

    /// <summary>
    ///   Occurs when an item has been deleted.
    /// </summary>
    public event EventHandler<ListChangedEventArgs<T>> ItemDeleted;

    /// <summary>
    ///   Occurs when the list has been reset.
    /// </summary>
    public event EventHandler<ListChangedEventArgs<T>> Reset;

    /// <summary>
    ///   Gets or sets the capacity.
    /// </summary>
    /// <value>The capacity.</value>
    public int Capacity
    {
      get { return _items.Length; }
      set
      {
        if (value != _items.Length)
        {
          if (value < _size)
          {
            throw new ArgumentOutOfRangeException("value");
          }
          if (value > 0)
          {
            var destinationArray = new T[value];
            if (_size > 0)
            {
              Array.Copy(_items, 0, destinationArray, 0, _size);
            }
            _items = destinationArray;
          }
          else
          {
            _items = _emptyArray;
          }
        }
      }
    }

    /// <summary>
    ///   Gets the number of elements contained in the <see cref = "T:System.Collections.Generic.ICollection`1" />.
    /// </summary>
    /// <value>The number of elements contained in the <see cref = "T:System.Collections.Generic.ICollection`1" />.</value>
    public int Count
    {
      get { return _size; }
    }

    /// <summary>
    ///   Gets a value indicating whether access to the <see cref = "T:System.Collections.ICollection" /> is synchronized (thread safe).
    /// </summary>
    /// <value></value>
    /// <returns>true if access to the <see cref = "T:System.Collections.ICollection" /> is synchronized (thread safe); otherwise, false.</returns>
    bool ICollection.IsSynchronized
    {
      get { return false; }
    }

    /// <summary>
    ///   Gets an object that can be used to synchronize access to the <see cref = "T:System.Collections.ICollection" />.
    /// </summary>
    /// <value></value>
    /// <returns>An object that can be used to synchronize access to the <see cref = "T:System.Collections.ICollection" />.</returns>
    object ICollection.SyncRoot
    {
      get
      {
        if (_syncRoot == null)
        {
          Interlocked.CompareExchange(ref _syncRoot, new object(), null);
        }
        return _syncRoot;
      }
    }

    /// <summary>
    ///   Gets a value indicating whether the <see cref = "T:System.Collections.Generic.ICollection`1" /> is read-only.
    /// </summary>
    /// <value></value>
    /// <returns>true if the <see cref = "T:System.Collections.Generic.ICollection`1" /> is read-only; otherwise, false.</returns>
    bool ICollection<T>.IsReadOnly
    {
      get { return false; }
    }

    /// <summary>
    ///   Gets a value indicating whether the <see cref = "T:System.Collections.IList" /> has a fixed size.
    /// </summary>
    /// <value></value>
    /// <returns>true if the <see cref = "T:System.Collections.IList" /> has a fixed size; otherwise, false.</returns>
    bool IList.IsFixedSize
    {
      get { return false; }
    }

    /// <summary>
    ///   Gets a value indicating whether the <see cref = "T:System.Collections.Generic.ICollection`1" /> is read-only.
    /// </summary>
    /// <value></value>
    /// <returns>true if the <see cref = "T:System.Collections.Generic.ICollection`1" /> is read-only; otherwise, false.</returns>
    bool IList.IsReadOnly
    {
      get { return false; }
    }

    /// <summary>
    ///   Gets or sets the <see cref = "System.Object" /> at the specified index.
    /// </summary>
    /// <value></value>
    object IList.this[int index]
    {
      get { return this[index]; }
      set
      {
        VerifyValueType(value);
        this[index] = (T) value;
      }
    }

    /// <summary>
    ///   Gets or sets the element at the specified index.
    /// </summary>
    /// <param name = "index">The zero-based index of the element to get or set.</param>
    /// <value>The element at the specified index.</value>
    public T this[int index]
    {
      get
      {
        if (index >= _size)
        {
          throw new ArgumentOutOfRangeException("index");
        }
        return _items[index];
      }
      set
      {
        if (index >= _size)
        {
          throw new ArgumentOutOfRangeException("index");
        }
        var oldValue = _items[index];
        _items[index] = value;
        _version++;
        OnItemChanged(index, oldValue, value);
      }
    }

    /// <summary>
    ///   Adds an item to the <see cref = "T:System.Collections.Generic.ICollection`1" />.
    /// </summary>
    /// <param name = "item">The object to add to the <see cref = "T:System.Collections.Generic.ICollection`1" />.</param>
    /// <exception cref = "T:System.NotSupportedException">The <see cref = "T:System.Collections.Generic.ICollection`1" /> is read-only.</exception>
    public void Add(T item)
    {
      if (_size == _items.Length)
      {
        EnsureCapacity(_size + 1);
      }
      _items[_size++] = item;
      _version++;
      OnItemAdded(_size, item);
    }

    /// <summary>
    ///   Adds the range.
    /// </summary>
    /// <param name = "collection">The collection.</param>
    public void AddRange(IEnumerable<T> collection)
    {
      InsertRange(_size, collection);
    }

    /// <summary>
    ///   Ases the read only.
    /// </summary>
    /// <returns></returns>
    public ReadOnlyCollection<T> AsReadOnly()
    {
      return new ReadOnlyCollection<T>(this);
    }

    /// <summary>
    ///   Binaries the search.
    /// </summary>
    /// <param name = "item">The item.</param>
    /// <returns></returns>
    public int BinarySearch(T item)
    {
      return BinarySearch(0, Count, item, null);
    }

    /// <summary>
    ///   Binaries the search.
    /// </summary>
    /// <param name = "item">The item.</param>
    /// <param name = "comparer">The comparer.</param>
    /// <returns></returns>
    public int BinarySearch(T item, IComparer<T> comparer)
    {
      return BinarySearch(0, Count, item, comparer);
    }

    /// <summary>
    ///   Binaries the search.
    /// </summary>
    /// <param name = "index">The index.</param>
    /// <param name = "count">The count.</param>
    /// <param name = "item">The item.</param>
    /// <param name = "comparer">The comparer.</param>
    /// <returns></returns>
    public int BinarySearch(int index, int count, T item, IComparer<T> comparer)
    {
      if ((index < 0) || (count < 0))
      {
        throw new ArgumentOutOfRangeException((index < 0) ? "index" : "count");
      }
      if ((_size - index) < count)
      {
        throw new ArgumentException();
      }
      return Array.BinarySearch(_items, index, count, item, comparer);
    }

    /// <summary>
    ///   Removes all items from the <see cref = "T:System.Collections.Generic.ICollection`1" />.
    /// </summary>
    /// <exception cref = "T:System.NotSupportedException">The <see cref = "T:System.Collections.Generic.ICollection`1" /> is read-only. </exception>
    public void Clear()
    {
      Array.Clear(_items, 0, _size);
      _size = 0;
      _version++;
      OnReset();
    }

    /// <summary>
    ///   Determines whether the <see cref = "T:System.Collections.Generic.ICollection`1" /> contains a specific value.
    /// </summary>
    /// <param name = "item">The object to locate in the <see cref = "T:System.Collections.Generic.ICollection`1" />.</param>
    /// <returns>
    ///   true if <paramref name = "item" /> is found in the <see cref = "T:System.Collections.Generic.ICollection`1" />; otherwise, false.
    /// </returns>
    public bool Contains(T item)
    {
      if (item == null)
      {
        for (var j = 0; j < _size; j++)
        {
          if (_items[j] == null)
          {
            return true;
          }
        }
        return false;
      }
      var comparer = EqualityComparer<T>.Default;
      for (var i = 0; i < _size; i++)
      {
        if (comparer.Equals(_items[i], item))
        {
          return true;
        }
      }
      return false;
    }

    /// <summary>
    ///   Converts all.
    /// </summary>
    /// <typeparam name = "TOutput">The type of the output.</typeparam>
    /// <param name = "converter">The converter.</param>
    /// <returns></returns>
    public EventedList<TOutput> ConvertAll<TOutput>(Converter<T, TOutput> converter)
    {
      if (converter == null)
      {
        throw new ArgumentNullException("converter");
      }
      var list = new EventedList<TOutput>(_size);
      for (var i = 0; i < _size; i++)
      {
        list._items[i] = converter(_items[i]);
      }
      list._size = _size;
      return list;
    }

    /// <summary>
    ///   Copies to.
    /// </summary>
    /// <param name = "array">The array.</param>
    public void CopyTo(T[] array)
    {
      CopyTo(array, 0);
    }

    /// <summary>
    ///   Copies the elements of the <see cref = "T:System.Collections.Generic.ICollection`1" /> to an <see cref = "T:System.Array" />, starting at a particular <see cref = "T:System.Array" /> index.
    /// </summary>
    /// <param name = "array">The one-dimensional <see cref = "T:System.Array" /> that is the destination of the elements copied from <see cref = "T:System.Collections.Generic.ICollection`1" />. The <see cref = "T:System.Array" /> must have zero-based indexing.</param>
    /// <param name = "arrayIndex">The zero-based index in <paramref name = "array" /> at which copying begins.</param>
    /// <exception cref = "T:System.ArgumentNullException">
    ///   <paramref name = "array" /> is null.</exception>
    /// <exception cref = "T:System.ArgumentOutOfRangeException">
    ///   <paramref name = "arrayIndex" /> is less than 0.</exception>
    /// <exception cref = "T:System.ArgumentException">
    ///   <paramref name = "array" /> is multidimensional.-or-<paramref name = "arrayIndex" /> is equal to or greater than the length of <paramref name = "array" />.-or-The number of elements in the source <see cref = "T:System.Collections.Generic.ICollection`1" /> is greater than the available space from <paramref name = "arrayIndex" /> to the end of the destination <paramref name = "array" />.-or-Type <c>T</c> cannot be cast automatically to the type of the destination <paramref name = "array" />.</exception>
    public void CopyTo(T[] array, int arrayIndex)
    {
      Array.Copy(_items, 0, array, arrayIndex, _size);
    }

    /// <summary>
    ///   Copies to.
    /// </summary>
    /// <param name = "index">The index.</param>
    /// <param name = "array">The array.</param>
    /// <param name = "arrayIndex">Index of the array.</param>
    /// <param name = "count">The count.</param>
    public void CopyTo(int index, T[] array, int arrayIndex, int count)
    {
      if ((_size - index) < count)
      {
        throw new ArgumentException();
      }
      Array.Copy(_items, index, array, arrayIndex, count);
    }

    /// <summary>
    ///   Existses the specified match.
    /// </summary>
    /// <param name = "match">The match.</param>
    /// <returns></returns>
    public bool Exists(Predicate<T> match)
    {
      return (FindIndex(match) != -1);
    }

    /// <summary>
    ///   Finds the specified match.
    /// </summary>
    /// <param name = "match">The match.</param>
    /// <returns></returns>
    public T Find(Predicate<T> match)
    {
      if (match == null)
      {
        throw new ArgumentNullException("match");
      }
      for (var i = 0; i < _size; i++)
      {
        if (match(_items[i]))
        {
          return _items[i];
        }
      }
      return default(T);
    }

    /// <summary>
    ///   Finds all.
    /// </summary>
    /// <param name = "match">The match.</param>
    /// <returns></returns>
    public EventedList<T> FindAll(Predicate<T> match)
    {
      if (match == null)
      {
        throw new ArgumentNullException("match");
      }
      var list = new EventedList<T>();
      for (var i = 0; i < _size; i++)
      {
        if (match(_items[i]))
        {
          list.Add(_items[i]);
        }
      }
      return list;
    }

    /// <summary>
    ///   Finds the index.
    /// </summary>
    /// <param name = "match">The match.</param>
    /// <returns></returns>
    public int FindIndex(Predicate<T> match)
    {
      return FindIndex(0, _size, match);
    }

    /// <summary>
    ///   Finds the index.
    /// </summary>
    /// <param name = "startIndex">The start index.</param>
    /// <param name = "match">The match.</param>
    /// <returns></returns>
    public int FindIndex(int startIndex, Predicate<T> match)
    {
      return FindIndex(startIndex, _size - startIndex, match);
    }

    /// <summary>
    ///   Finds the index.
    /// </summary>
    /// <param name = "startIndex">The start index.</param>
    /// <param name = "count">The count.</param>
    /// <param name = "match">The match.</param>
    /// <returns></returns>
    public int FindIndex(int startIndex, int count, Predicate<T> match)
    {
      if (startIndex > _size)
      {
        throw new ArgumentOutOfRangeException("startIndex");
      }
      if ((count < 0) || (startIndex > (_size - count)))
      {
        throw new ArgumentOutOfRangeException("count");
      }
      if (match == null)
      {
        throw new ArgumentNullException("match");
      }
      var num = startIndex + count;
      for (var i = startIndex; i < num; i++)
      {
        if (match(_items[i]))
        {
          return i;
        }
      }
      return -1;
    }

    /// <summary>
    ///   Finds the last.
    /// </summary>
    /// <param name = "match">The match.</param>
    /// <returns></returns>
    public T FindLast(Predicate<T> match)
    {
      if (match == null)
      {
        throw new ArgumentNullException("match");
      }
      for (var i = _size - 1; i >= 0; i--)
      {
        if (match(_items[i]))
        {
          return _items[i];
        }
      }
      return default(T);
    }

    /// <summary>
    ///   Finds the last index.
    /// </summary>
    /// <param name = "match">The match.</param>
    /// <returns></returns>
    public int FindLastIndex(Predicate<T> match)
    {
      return FindLastIndex(_size - 1, _size, match);
    }

    /// <summary>
    ///   Finds the last index.
    /// </summary>
    /// <param name = "startIndex">The start index.</param>
    /// <param name = "match">The match.</param>
    /// <returns></returns>
    public int FindLastIndex(int startIndex, Predicate<T> match)
    {
      return FindLastIndex(startIndex, startIndex + 1, match);
    }

    /// <summary>
    ///   Finds the last index.
    /// </summary>
    /// <param name = "startIndex">The start index.</param>
    /// <param name = "count">The count.</param>
    /// <param name = "match">The match.</param>
    /// <returns></returns>
    public int FindLastIndex(int startIndex, int count, Predicate<T> match)
    {
      if (match == null)
      {
        throw new ArgumentNullException("match");
      }
      if (_size == 0)
      {
        if (startIndex != -1)
        {
          throw new ArgumentOutOfRangeException("startIndex");
        }
      }
      else if (startIndex >= _size)
      {
        throw new ArgumentOutOfRangeException("startIndex");
      }
      if ((count < 0) || (((startIndex - count) + 1) < 0))
      {
        throw new ArgumentOutOfRangeException("count");
      }
      var num = startIndex - count;
      for (var i = startIndex; i > num; i--)
      {
        if (match(_items[i]))
        {
          return i;
        }
      }
      return -1;
    }

    /// <summary>
    ///   Fors the each.
    /// </summary>
    /// <param name = "action">The action.</param>
    public void ForEach(Action<T> action)
    {
      if (action == null)
      {
        throw new ArgumentNullException("match");
      }
      for (var i = 0; i < _size; i++)
      {
        action(_items[i]);
      }
    }

    /// <summary>
    ///   Gets the enumerator.
    /// </summary>
    /// <returns></returns>
    public Enumerator GetEnumerator()
    {
      return new Enumerator(this);
    }

    /// <summary>
    ///   Gets the range of items and returns then in another list.
    /// </summary>
    /// <param name = "index">The starting index.</param>
    /// <param name = "count">The count of items to place in the list.</param>
    /// <returns>An <see cref = "EventedList&lt;T&gt;" /> with the requested items.</returns>
    public EventedList<T> GetRange(int index, int count)
    {
      if ((index < 0) || (count < 0))
      {
        throw new ArgumentOutOfRangeException((index < 0) ? "index" : "count");
      }
      if ((_size - index) < count)
      {
        throw new ArgumentException();
      }
      var list = new EventedList<T>(count);
      Array.Copy(_items, index, list._items, 0, count);
      list._size = count;
      return list;
    }

    /// <summary>
    ///   Copies to.
    /// </summary>
    /// <param name = "array">The array.</param>
    /// <param name = "arrayIndex">Index of the array.</param>
    void ICollection.CopyTo(Array array, int arrayIndex)
    {
      if ((array != null) && (array.Rank != 1))
      {
        throw new ArgumentException();
      }
      try
      {
        Array.Copy(_items, 0, array, arrayIndex, _size);
      }
      catch (ArrayTypeMismatchException)
      {
        throw new ArgumentException();
      }
    }

    /// <summary>
    ///   Returns an enumerator that iterates through a collection.
    /// </summary>
    /// <returns>
    ///   An <see cref = "T:System.Collections.IEnumerator" /> object that can be used to iterate through the collection.
    /// </returns>
    IEnumerator IEnumerable.GetEnumerator()
    {
      return new Enumerator(this);
    }

    /// <summary>
    ///   Returns an enumerator that iterates through the collection.
    /// </summary>
    /// <returns>
    ///   A <see cref = "T:System.Collections.Generic.IEnumerator`1" /> that can be used to iterate through the collection.
    /// </returns>
    IEnumerator<T> IEnumerable<T>.GetEnumerator()
    {
      return new Enumerator(this);
    }

    /// <summary>
    ///   Adds the specified item.
    /// </summary>
    /// <param name = "item">The item.</param>
    /// <returns></returns>
    int IList.Add(object item)
    {
      VerifyValueType(item);
      Add((T) item);
      return (Count - 1);
    }

    /// <summary>
    ///   Determines whether [contains] [the specified item].
    /// </summary>
    /// <param name = "item">The item.</param>
    /// <returns>
    ///   <c>true</c> if [contains] [the specified item]; otherwise, <c>false</c>.
    /// </returns>
    bool IList.Contains(object item)
    {
      return (IsCompatibleObject(item) && Contains((T) item));
    }

    /// <summary>
    ///   Indexes the of.
    /// </summary>
    /// <param name = "item">The item.</param>
    /// <returns></returns>
    int IList.IndexOf(object item)
    {
      if (IsCompatibleObject(item))
      {
        return IndexOf((T) item);
      }
      return -1;
    }

    /// <summary>
    ///   Inserts the specified index.
    /// </summary>
    /// <param name = "index">The index.</param>
    /// <param name = "item">The item.</param>
    void IList.Insert(int index, object item)
    {
      VerifyValueType(item);
      Insert(index, (T) item);
    }

    /// <summary>
    ///   Removes the specified item.
    /// </summary>
    /// <param name = "item">The item.</param>
    void IList.Remove(object item)
    {
      if (IsCompatibleObject(item))
      {
        Remove((T) item);
      }
    }

    /// <summary>
    ///   Determines the index of a specific item in the <see cref = "T:System.Collections.Generic.IList`1" />.
    /// </summary>
    /// <param name = "item">The object to locate in the <see cref = "T:System.Collections.Generic.IList`1" />.</param>
    /// <returns>
    ///   The index of <paramref name = "item" /> if found in the list; otherwise, -1.
    /// </returns>
    public int IndexOf(T item)
    {
      return Array.IndexOf(_items, item, 0, _size);
    }

    /// <summary>
    ///   Indexes the of.
    /// </summary>
    /// <param name = "item">The item.</param>
    /// <param name = "index">The index.</param>
    /// <returns></returns>
    public int IndexOf(T item, int index)
    {
      if (index > _size)
      {
        throw new ArgumentOutOfRangeException("index");
      }
      return Array.IndexOf(_items, item, index, _size - index);
    }

    /// <summary>
    ///   Indexes the of.
    /// </summary>
    /// <param name = "item">The item.</param>
    /// <param name = "index">The index.</param>
    /// <param name = "count">The count.</param>
    /// <returns></returns>
    public int IndexOf(T item, int index, int count)
    {
      if (index > _size)
      {
        throw new ArgumentOutOfRangeException("index");
      }
      if ((count < 0) || (index > (_size - count)))
      {
        throw new ArgumentOutOfRangeException("count");
      }
      return Array.IndexOf(_items, item, index, count);
    }

    /// <summary>
    ///   Inserts an item to the <see cref = "T:System.Collections.Generic.IList`1" /> at the specified index.
    /// </summary>
    /// <param name = "index">The zero-based index at which <paramref name = "item" /> should be inserted.</param>
    /// <param name = "item">The object to insert into the <see cref = "T:System.Collections.Generic.IList`1" />.</param>
    /// <exception cref = "T:System.ArgumentOutOfRangeException">
    ///   <paramref name = "index" /> is not a valid index in the <see cref = "T:System.Collections.Generic.IList`1" />.</exception>
    /// <exception cref = "T:System.NotSupportedException">The <see cref = "T:System.Collections.Generic.IList`1" /> is read-only.</exception>
    public void Insert(int index, T item)
    {
      if (index > _size)
      {
        throw new ArgumentOutOfRangeException("index");
      }
      if (_size == _items.Length)
      {
        EnsureCapacity(_size + 1);
      }
      if (index < _size)
      {
        Array.Copy(_items, index, _items, index + 1, _size - index);
      }
      _items[index] = item;
      _size++;
      _version++;
      OnItemAdded(index, item);
    }

    /// <summary>
    ///   Inserts the range.
    /// </summary>
    /// <param name = "index">The index.</param>
    /// <param name = "collection">The collection.</param>
    public void InsertRange(int index, IEnumerable<T> collection)
    {
      if (collection == null)
      {
        throw new ArgumentNullException("collection");
      }
      if (index > _size)
      {
        throw new ArgumentOutOfRangeException("index");
      }
      var is2 = collection as ICollection<T>;
      if (is2 != null)
      {
        var count = is2.Count;
        if (count > 0)
        {
          EnsureCapacity(_size + count);
          if (index < _size)
          {
            Array.Copy(_items, index, _items, index + count, _size - index);
          }
          if (this == is2)
          {
            Array.Copy(_items, 0, _items, index, index);
            Array.Copy(_items, (index + count), _items, (index*2), (_size - index));
          }
          else
          {
            var array = new T[count];
            is2.CopyTo(array, 0);
            array.CopyTo(_items, index);
          }
          _size += count;
          for (var i = index; i < index + count; i++)
            OnItemAdded(i, _items[i]);
        }
      }
      else
      {
        using (var enumerator = collection.GetEnumerator())
        {
          while (enumerator.MoveNext())
          {
            Insert(index++, enumerator.Current);
          }
        }
      }
      _version++;
    }

    /// <summary>
    ///   Lasts the index of.
    /// </summary>
    /// <param name = "item">The item.</param>
    /// <returns></returns>
    public int LastIndexOf(T item)
    {
      return LastIndexOf(item, _size - 1, _size);
    }

    /// <summary>
    ///   Lasts the index of.
    /// </summary>
    /// <param name = "item">The item.</param>
    /// <param name = "index">The index.</param>
    /// <returns></returns>
    public int LastIndexOf(T item, int index)
    {
      if (index >= _size)
      {
        throw new ArgumentOutOfRangeException("index");
      }
      return LastIndexOf(item, index, index + 1);
    }

    /// <summary>
    ///   Lasts the index of.
    /// </summary>
    /// <param name = "item">The item.</param>
    /// <param name = "index">The index.</param>
    /// <param name = "count">The count.</param>
    /// <returns></returns>
    public int LastIndexOf(T item, int index, int count)
    {
      if (_size == 0)
      {
        return -1;
      }
      if ((index < 0) || (count < 0))
      {
        throw new ArgumentOutOfRangeException((index < 0) ? "index" : "count");
      }
      if ((index >= _size) || (count > (index + 1)))
      {
        throw new ArgumentOutOfRangeException((index >= _size) ? "index" : "count");
      }
      return Array.LastIndexOf(_items, item, index, count);
    }

    /// <summary>
    ///   Removes the first occurrence of a specific object from the <see cref = "T:System.Collections.Generic.ICollection`1" />.
    /// </summary>
    /// <param name = "item">The object to remove from the <see cref = "T:System.Collections.Generic.ICollection`1" />.</param>
    /// <returns>
    ///   true if <paramref name = "item" /> was successfully removed from the <see cref = "T:System.Collections.Generic.ICollection`1" />; otherwise, false. This method also returns false if <paramref name = "item" /> is not found in the original <see cref = "T:System.Collections.Generic.ICollection`1" />.
    /// </returns>
    /// <exception cref = "T:System.NotSupportedException">The <see cref = "T:System.Collections.Generic.ICollection`1" /> is read-only.</exception>
    public bool Remove(T item)
    {
      var index = IndexOf(item);
      if (index >= 0)
      {
        RemoveAt(index);
        return true;
      }
      return false;
    }

    /// <summary>
    ///   Removes all.
    /// </summary>
    /// <param name = "match">The match.</param>
    /// <returns></returns>
    public int RemoveAll(Predicate<T> match)
    {
      if (match == null)
      {
        throw new ArgumentNullException("match");
      }
      var index = 0;
      while ((index < _size) && !match(_items[index]))
      {
        index++;
      }
      if (index >= _size)
      {
        return 0;
      }
      var num2 = index + 1;
      while (num2 < _size)
      {
        while ((num2 < _size) && match(_items[num2]))
        {
          num2++;
        }
        if (num2 < _size)
        {
          var oldVal = _items[index + 1];
          _items[index++] = _items[num2++];
          OnItemDeleted(index, oldVal);
        }
      }
      Array.Clear(_items, index, _size - index);
      var num3 = _size - index;
      _size = index;
      _version++;
      return num3;
    }

    /// <summary>
    ///   Removes the <see cref = "T:System.Collections.Generic.IList`1" /> item at the specified index.
    /// </summary>
    /// <param name = "index">The zero-based index of the item to remove.</param>
    /// <exception cref = "T:System.ArgumentOutOfRangeException">
    ///   <paramref name = "index" /> is not a valid index in the <see cref = "T:System.Collections.Generic.IList`1" />.</exception>
    /// <exception cref = "T:System.NotSupportedException">The <see cref = "T:System.Collections.Generic.IList`1" /> is read-only.</exception>
    public void RemoveAt(int index)
    {
      if (index >= _size)
      {
        throw new ArgumentOutOfRangeException("index");
      }
      _size--;
      var oldVal = _items[index];
      if (index < _size)
      {
        Array.Copy(_items, index + 1, _items, index, _size - index);
      }
      _items[_size] = default(T);
      _version++;
      OnItemDeleted(index, oldVal);
    }

    /// <summary>
    ///   Removes the range.
    /// </summary>
    /// <param name = "index">The index.</param>
    /// <param name = "count">The count.</param>
    public void RemoveRange(int index, int count)
    {
      if ((index < 0) || (count < 0))
      {
        throw new ArgumentOutOfRangeException((index < 0) ? "index" : "count");
      }
      if ((_size - index) < count)
      {
        throw new ArgumentException();
      }
      if (count > 0)
      {
        _size -= count;
        var array = new T[count];
        Array.Copy(_items, index, array, 0, count);
        if (index < _size)
        {
          Array.Copy(_items, index + count, _items, index, _size - index);
        }
        Array.Clear(_items, _size, count);
        _version++;
        for (var i = index; i < index + count; i++)
          OnItemDeleted(i, array[i - index]);
      }
    }

    /// <summary>
    ///   Reverses this instance.
    /// </summary>
    public void Reverse()
    {
      Reverse(0, Count);
    }

    /// <summary>
    ///   Reverses the specified index.
    /// </summary>
    /// <param name = "index">The index.</param>
    /// <param name = "count">The count.</param>
    public void Reverse(int index, int count)
    {
      if ((index < 0) || (count < 0))
      {
        throw new ArgumentOutOfRangeException((index < 0) ? "index" : "count");
      }
      if ((_size - index) < count)
      {
        throw new ArgumentException();
      }
      Array.Reverse(_items, index, count);
      _version++;
    }

    /// <summary>
    ///   Sorts this instance.
    /// </summary>
    public void Sort()
    {
      Sort(0, Count, null);
    }

    /// <summary>
    ///   Sorts the specified comparer.
    /// </summary>
    /// <param name = "comparer">The comparer.</param>
    public void Sort(IComparer<T> comparer)
    {
      Sort(0, Count, comparer);
    }

    /// <summary>
    ///   Sorts the specified index.
    /// </summary>
    /// <param name = "index">The index.</param>
    /// <param name = "count">The count.</param>
    /// <param name = "comparer">The comparer.</param>
    public void Sort(int index, int count, IComparer<T> comparer)
    {
      if ((index < 0) || (count < 0))
      {
        throw new ArgumentOutOfRangeException((index < 0) ? "index" : "count");
      }
      if ((_size - index) < count)
      {
        throw new ArgumentException();
      }
      Array.Sort(_items, index, count, comparer);
      _version++;
    }

    /// <summary>
    ///   Toes the array.
    /// </summary>
    /// <returns></returns>
    public T[] ToArray()
    {
      var destinationArray = new T[_size];
      Array.Copy(_items, 0, destinationArray, 0, _size);
      return destinationArray;
    }

    /// <summary>
    ///   Trims the excess.
    /// </summary>
    public void TrimExcess()
    {
      var num = (int) (_items.Length*0.9);
      if (_size < num)
      {
        Capacity = _size;
      }
    }

    /// <summary>
    ///   Trues for all.
    /// </summary>
    /// <param name = "match">The match.</param>
    /// <returns></returns>
    public bool TrueForAll(Predicate<T> match)
    {
      if (match == null)
      {
        throw new ArgumentNullException("match");
      }
      for (var i = 0; i < _size; i++)
      {
        if (!match(_items[i]))
        {
          return false;
        }
      }
      return true;
    }

    /// <summary>
    ///   Called when [insert].
    /// </summary>
    /// <param name = "index">The index.</param>
    /// <param name = "value">The value.</param>
    protected virtual void OnItemAdded(int index, T value)
    {
      var h = ItemAdded;
      if (h != null)
        h(this, new ListChangedEventArgs<T>(ListChangedType.ItemAdded, value, index));
    }

    /// <summary>
    ///   Called when [set].
    /// </summary>
    /// <param name = "index">The index.</param>
    /// <param name = "oldValue">The old value.</param>
    /// <param name = "newValue">The new value.</param>
    protected virtual void OnItemChanged(int index, T oldValue, T newValue)
    {
      var h = ItemChanged;
      if (h != null)
        h(this, new ListChangedEventArgs<T>(ListChangedType.ItemChanged, newValue, index, oldValue));
    }

    /// <summary>
    ///   Called when [remove].
    /// </summary>
    /// <param name = "index">The index.</param>
    /// <param name = "value">The value.</param>
    protected virtual void OnItemDeleted(int index, T value)
    {
      var h = ItemDeleted;
      if (h != null)
        h(this, new ListChangedEventArgs<T>(ListChangedType.ItemDeleted, value, index));
    }

    /// <summary>
    ///   Called when [clear].
    /// </summary>
    protected virtual void OnReset()
    {
      var h = Reset;
      if (h != null)
        h(this, new ListChangedEventArgs<T>(ListChangedType.Reset));
    }

    /// <summary>
    ///   Determines whether [is compatible object] [the specified value].
    /// </summary>
    /// <param name = "value">The value.</param>
    /// <returns>
    ///   <c>true</c> if [is compatible object] [the specified value]; otherwise, <c>false</c>.
    /// </returns>
    private static bool IsCompatibleObject(object value)
    {
      if (!(value is T) && ((value != null) || typeof (T).IsValueType))
      {
        return false;
      }
      return true;
    }

    /// <summary>
    ///   Verifies the type of the value.
    /// </summary>
    /// <param name = "value">The value.</param>
    private static void VerifyValueType(object value)
    {
      if (!IsCompatibleObject(value))
      {
        throw new ArgumentException("value");
      }
    }

    // Methods
    /// <summary>
    ///   Ensures the capacity.
    /// </summary>
    /// <param name = "min">The min.</param>
    private void EnsureCapacity(int min)
    {
      if (_items.Length < min)
      {
        var num = (_items.Length == 0) ? 4 : (_items.Length*2);
        if (num < min)
        {
          num = min;
        }
        Capacity = num;
      }
    }

    /// <summary>
    ///   Enumerates over the <see cref = "EventedList&lt;T&gt;" />.
    /// </summary>
    [Serializable,
     StructLayout(LayoutKind.Sequential)]
    public struct Enumerator : IEnumerator<T>, IDisposable, IEnumerator
    {
      private readonly EventedList<T> list;
      private int index;
      private readonly int version;
      private T current;

      /// <summary>
      ///   Initializes a new instance of the <see cref = "EventedList&lt;T&gt;.Enumerator" /> struct.
      /// </summary>
      /// <param name = "list">The list.</param>
      internal Enumerator(EventedList<T> list)
      {
        this.list = list;
        index = 0;
        version = list._version;
        current = default(T);
      }

      /// <summary>
      ///   Gets the current.
      /// </summary>
      /// <value>The current.</value>
      public T Current
      {
        get { return current; }
      }

      /// <summary>
      ///   Gets the current.
      /// </summary>
      /// <value>The current.</value>
      object IEnumerator.Current
      {
        get
        {
          if ((index == 0) || (index == (list._size + 1)))
          {
            throw new InvalidOperationException();
          }
          return Current;
        }
      }

      /// <summary>
      ///   Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
      /// </summary>
      public void Dispose()
      {
      }

      /// <summary>
      ///   Sets the enumerator to its initial position, which is before the first element in the collection.
      /// </summary>
      /// <exception cref = "T:System.InvalidOperationException">The collection was modified after the enumerator was created. </exception>
      void IEnumerator.Reset()
      {
        if (version != list._version)
        {
          throw new InvalidOperationException();
        }
        index = 0;
        current = default(T);
      }

      /// <summary>
      ///   Advances the enumerator to the next element of the collection.
      /// </summary>
      /// <returns>
      ///   true if the enumerator was successfully advanced to the next element; false if the enumerator has passed the end of the collection.
      /// </returns>
      /// <exception cref = "T:System.InvalidOperationException">The collection was modified after the enumerator was created. </exception>
      public bool MoveNext()
      {
        if (version != list._version)
        {
          throw new InvalidOperationException();
        }
        if (index < list._size)
        {
          current = list._items[index];
          index++;
          return true;
        }
        index = list._size + 1;
        current = default(T);
        return false;
      }
    }

    /// <summary>
    /// An <see cref="EventArgs"/> structure passed to events generated by an <see cref="EventedList{T}"/>.
    /// </summary>
    /// <typeparam name="T"></typeparam>
#pragma warning disable 693
    public class ListChangedEventArgs<T> : EventArgs
    {
      /// <summary>
      ///   Initializes a new instance of the <see cref = "EventedList&lt;T&gt;.ListChangedEventArgs&lt;T&gt;" /> class.
      /// </summary>
      /// <param name = "type">The type of change.</param>
      public ListChangedEventArgs(ListChangedType type)
      {
        ItemIndex = -1;
        ListChangedType = type;
      }

      /// <summary>
      ///   Initializes a new instance of the <see cref = "EventedList&lt;T&gt;.ListChangedEventArgs&lt;T&gt;" /> class.
      /// </summary>
      /// <param name = "type">The type of change.</param>
      /// <param name = "item">The item that has changed.</param>
      /// <param name = "itemIndex">Index of the changed item.</param>
      public ListChangedEventArgs(ListChangedType type, T item, int itemIndex)
      {
        Item = item;
        ItemIndex = itemIndex;
        ListChangedType = type;
      }

      /// <summary>
      ///   Initializes a new instance of the <see cref = "EventedList&lt;T&gt;.ListChangedEventArgs&lt;T&gt;" /> class.
      /// </summary>
      /// <param name = "type">The type of change.</param>
      /// <param name = "item">The item that has changed.</param>
      /// <param name = "itemIndex">Index of the changed item.</param>
      /// <param name = "oldItem">The old item when an item has changed.</param>
      public ListChangedEventArgs(ListChangedType type, T item, int itemIndex, T oldItem)
        : this(type, item, itemIndex)
      {
        OldItem = oldItem;
      }

      /// <summary>
      ///   Gets the item that has changed.
      /// </summary>
      /// <value>The item.</value>
      public T Item { get; private set; }

      /// <summary>
      ///   Gets the index of the item.
      /// </summary>
      /// <value>The index of the item.</value>
      public int ItemIndex { get; private set; }

      /// <summary>
      ///   Gets the type of change for the list.
      /// </summary>
      /// <value>The type of change for the list.</value>
      public ListChangedType ListChangedType { get; private set; }

      /// <summary>
      ///   Gets the item's previous value.
      /// </summary>
      /// <value>The old item.</value>
      public T OldItem { get; private set; }
    }
#pragma warning restore 693
  }
}